package com.sangmo.boot.auth.core;

import com.sangmo.boot.auth.core.errors.ErrorMessages;
import com.sangmo.boot.auth.core.handlers.AuthorizeHandler;
import com.sangmo.boot.auth.core.interfaces.GrantTokenRequest;
import com.sangmo.boot.auth.core.interfaces.GrantTokenResponse;
import com.sangmo.boot.auth.core.interfaces.ValidateTokenRequest;
import com.sangmo.boot.auth.core.model.AccessToken;
import com.sangmo.boot.auth.core.model.Client;
import com.sangmo.boot.auth.core.repo.AccessTokenRepo;
import com.sangmo.boot.framework.common.errors.ServiceException;
import com.sangmo.boot.framework.common.errors.StandardErrors;
import com.sangmo.boot.framework.common.session.UserSession;
import com.sangmo.boot.framework.common.utils.UuidUtils;
import com.sangmo.boot.framework.plugin.dataview.crud.service.BaseViewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;

@Service
public class AccessTokenService extends BaseViewService<AccessToken> {

	@Autowired
	private AccessTokenRepo repo;

	@Override
	public AccessTokenRepo getRepo() {
		return repo;
	}

	@Resource(name = "authClientHandlers")
	private Map<String, AuthorizeHandler> handlerRegister;

	private AuthorizeHandler findHandlerForClient(String clientId) {
		AuthorizeHandler handler = handlerRegister.get(clientId);
		if (null == handler) {
			handler = handlerRegister.get("default");
		}
		return handler;
	}

	public void delete(String token) {
		repo.deleteByValue(token);
	}

	public GrantTokenResponse grant(GrantTokenRequest req) {
		AuthorizeHandler handler = this.findHandlerForClient(req.getClientId());
		return handler.grant(req);
	}

	public UserSession.Authorize<?> validate(ValidateTokenRequest req) {
		AuthorizeHandler handler = this.findHandlerForClient(req.getClientId());
		return handler.validate(req);
	}

	public AccessToken validateToken(String clientId, String token) {
		AccessToken accessToken = this.repo.findOneByClientIdAndValue(clientId, token);
		if(accessToken == null) {
			throw new ServiceException(StandardErrors.UNAUTHORIZED.getStatus(), ErrorMessages.AUTH_TOKEN_NOTEXISTS);
		}
		if(accessToken.isExpired()) {
			throw new ServiceException(StandardErrors.UNAUTHORIZED.getStatus(), ErrorMessages.AUTH_TOKEN_EXPIRED);
		}
		return accessToken;
	}

	public AccessToken createOrUpdateToken(Client client, GrantTokenRequest req, boolean alwaysCreateNew) {
		AccessToken token = repo.findOneByClientIdAndUsername(client.getId(), req.getUsername());
		if (null == token) {
			token = new AccessToken();
			token.setClientId(client.getId());
			token.setUsername(req.getUsername());
		}

		token.setValidity(client.getValidity());
		token.setScope(req.getScope());
		token.setResource(req.getResource());
		token.setRefreshTime(new Date());

		if (null == token.getId() || alwaysCreateNew || token.isExpired()) {
			token.setValue(UuidUtils.getUUID());
		}
		token.setType(AccessToken.Types.BEARER);
		return this.save(token);
	}
}
